/*
 * Getrap
 * Copyright 2008(C) Grzegorz 'milyges' Glinski
 *
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
*/
#include <job.h>
#include <QtDebug>
#include <config.h>
#include <QTextCodec>

char * Job::mStatusTable[] = { QT_TR_NOOP("Zatrzymany"), QT_TR_NOOP("W kolejce"),
                                QT_TR_NOOP("Oczekiwanie"), QT_TR_NOOP("Uruchomiony"),
                                QT_TR_NOOP("Zakonczony"), QT_TR_NOOP("Blad") };
int Job::mActiveJobs = 0;

int Job::activeJobs(void)
{
 return mActiveJobs;
}

QString Job::size2str(int size)
{
 if (size < 1024)
   return QString("%1B").arg(size);
 size /= 1024;
 if (size < 1024)
   return QString("%1KB").arg(size);
 size /= 1024;
 return QString("%1MB").arg(size);
}

QString Job::time2str(int secs)
{
 int m, h;
 m = secs / 60;
 secs = secs % 60;
 h = m / 60;
 m = m % 60;
 return QString("%1:%2:%3").arg(h, 2, 10, QLatin1Char('0')).arg(m, 2, 10, QLatin1Char('0')).arg(secs, 2, 10, QLatin1Char('0'));
}

Job::Job(QTreeWidget * parent, QString link) : QObject(parent), QTreeWidgetItem(parent)
{
 mFile = QString(link).replace(QRegExp("http\\://(.*\\.)?rapidshare.com/files/([0-9]*)/(.*)"), "\\3");
 mLink = QUrl(link);
 setText(0, mFile);
 setText(2, "--");
 setText(3, "--");
 setText(4, "--");
 setText(5, "--");
 setText(6, link);
 setStatus(jsStopped);
 mHttp = new QHttp(this);
 mOutputFile = new QFile(this);
 mTimer = new QTimer(this);
}

Job::~Job()
{
 delete mHttp;
 delete mTimer;
 delete mOutputFile;
}

void Job::httpError(void)
{
 mActiveJobs--;
 setStatus(jsError, mHttp->errorString());
}

void Job::setStatus(int status, QString msg)
{
 QColor c;
 mStatus = status;
 if (msg.isEmpty())
   setText(1, tr(mStatusTable[mStatus]));
 else
   setText(1, tr(mStatusTable[mStatus]) + " (" + msg + ")");
 /* Kolorki! */
 switch(mStatus)
 {
  case jsStopped: c = QColor(0xFF, 0xFF, 0x33); break;
  case jsDone: c = QColor(0x99, 0xFF, 0x99); break;
  case jsError: c = QColor(0xFF, 0x33, 0x33); break;
  default: c = QColor(0xFF, 0xFF, 0xFF);
 }
 setBackground(0, c);
 setBackground(1, c);
 setBackground(2, c);
 setBackground(3, c);
 setBackground(4, c);
 setBackground(5, c);
 setBackground(6, c);
}

void Job::start(void)
{
 int maxJobs;
 if (mStatus == jsStopped)
   setStatus(jsQueue);
 if (mStatus != jsQueue)
    return;
 maxJobs = (Config::getValue("UsePremium").toInt() != 0) ?  Config::getValue("PremiumThreads").toInt() : 1;
 if (mActiveJobs < maxJobs)
 {
  mActiveJobs++;
  setStatus(jsReadyWait);
  mSpeed = 0;
  mHttp->setHost(mLink.host());
  connect(mHttp, SIGNAL(done(bool)), this, SLOT(prepareDone(bool)));
  mHttp->get(mLink.path());
 }
}

void Job::stop(void)
{
 /* Jeżeli zadanie oczekuje w kolejce, zmienmy mu tylko status */
 if (mStatus == jsQueue)
 {
  setStatus(jsStopped);
  return;
 }
 /* Jezeli nie jest aktywne nie ma nic do roboty */
 if ((mStatus != jsRunning) && (mStatus != jsReadyWait))
    return;

 /* Jeżeli jakie kolwiek odliczanie jest aktywne, zatrzymujemy go */
 if (mTimer->isActive())
 {
  mTimer->stop();
  disconnect(mTimer, SIGNAL(timeout()), this, 0);
 }

 /* Jeżeli jest aktywny jakikolwiek transfer przez HTTP, anlujemy go */
 if (mHttp->hasPendingRequests())
 {
  mHttp->abort();
  disconnect(mHttp, SIGNAL(done(bool)), this, 0);
 }

 mOutputFile->setFileName(Config::getValue("OutputDir") + "/" + mFile);
 if (mOutputFile->exists())
 {
  mOutputFile->remove();
 }
 setText(2, "--");
 setText(3, "--");
 setText(4, "--");
 mSpeed = 0;
 setStatus(jsStopped);
 mActiveJobs--;

 /* Uruchamiamy nastepne zadanie */
 startNextJob();
}

void Job::prepareDone(bool error, bool restart)
{
 QString data;
 QHttpRequestHeader header;
 disconnect(mHttp, SIGNAL(done(bool)), this, 0);
 data = mHttp->readAll();
 if (!restart)
 {
  if (error)
  {
   httpError();
   return;
  }
  if (data.contains("file could not be found"))
  {
   //qDebug() << "File not found";
   setStatus(jsError, tr("Plik nie istnieje"));
   mActiveJobs--;
   startNextJob();
   return;
  }
  mLink = QUrl(data.replace(QRegExp("(.*) action=\"(.*)\" method=(.*)"), "\\2"));
 }
 /*
 if (Config::getValue("UsePremium") == "Yes")
 {
  data = "dl.start=PREMIUM";
 }
 else
 {*/
  data = "dl.start=Free";
  connect(mHttp, SIGNAL(done(bool)), this, SLOT(freeSelected(bool)));
 //}
 mHttp->setHost(mLink.host());
 header.setRequest("POST", mLink.path());
 header.setValue("Host", mLink.host());
 //header.setContentType("application/x-www-form-urlencoded");
 //mBuffer->
 mHttp->request(header, data.toUtf8());
}

void Job::freeSelected(bool error)
{
 QString data;
 disconnect(mHttp, SIGNAL(done(bool)), this, 0);
 if (error)
 {
  //qDebug() << "Job::freeSelected(): ERROR";
  httpError();
  return;
 }

 data = mHttp->readAll();
 if (data.contains("already downloading"))
 {
  // Sciagamy juz inny plik
  //qDebug() << "Your IP address is already downloading a file";
  mActiveJobs--;
  setStatus(jsQueue);
  emit fatalError(tr("Sciągasz aktualnie inny plik."));
  return;
 }

 if (data.contains(QRegExp("(.*)about ([0-9]*) min(.*)")))
 {
  //qDebug() << "You have reached the download limit for free-users.";
  mTimeout = QString(data).replace(QRegExp("(.*)about ([0-9]*) min(.*)"), "\\2").toInt() * 60;
  connect(mTimer, SIGNAL(timeout()), this, SLOT(waitForRestart()));
  mTimer->setInterval(1000);
  mTimer->start();
  return;
 }

 /* Pobierz czas oczekiwania i nowy link */
 mTimeout = QString(data).replace(QRegExp("(.*)var c=([0-9]*);(.*)"), "\\2").toInt();
 mLink = QUrl(QString(data).replace(QRegExp("(.*) action=\"(.*)\" method=(.*)"), "\\2"));
 setText(3, QString("%1s").arg(mTimeout));
 connect(mTimer, SIGNAL(timeout()), this, SLOT(freeWait()));
 mTimer->setInterval(1000);
 mTimer->start();
}

void Job::freeWait(void)
{
 QHttpRequestHeader header;
 //qDebug() << "Job::freeWait(void)" << mTimeout;
 //QFile outputFile;
 if (mTimeout <= 0)
 {
  disconnect(mTimer, SIGNAL(timeout()), this, 0);
  mTimer->stop();
  /* Rozpoczynamy prawdziwe pobieranie pliku */
  connect(mHttp, SIGNAL(done(bool)), this, SLOT(downloadDone(bool)));
  connect(mHttp, SIGNAL(dataReadProgress(int, int)), this, SLOT(progressUpdate(int,int)));
  header.setRequest("POST", mLink.path());
  header.setValue("Host", mLink.host());
  setStatus(jsRunning);
  mOutputFile->setFileName(Config::getValue("OutputDir") + "/" + mFile);
  if (!mOutputFile->open(QIODevice::WriteOnly | QIODevice::Truncate))
  {
   setStatus(jsStopped);
   emit fatalError(tr("Nie można utworzyć pliku docelowego ") + mFile);
   return;
  }
  mHttp->request(header, QString("mirror=on").toUtf8(), mOutputFile);
  connect(mTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
  mTimer->setInterval(1000);
  mTimer->start();
  return;
 }
 mTimeout--;
 setText(3, QString("%1s").arg(mTimeout));
}

void Job::downloadDone(bool error)
{
 mTimer->stop();
 disconnect(mTimer, SIGNAL(timeout()), this, 0);
 //qDebug() << "Job::downloadDone(" << error << ")";
 if (error)
 {
  mOutputFile->setFileName(Config::getValue("OutputDir") + "/" + mFile);
  if (mOutputFile->exists())
    mOutputFile->remove();
  return;
 }
 else
 {
  setStatus(jsDone);
  setText(2, text(5));
  setText(3, time2str(0));
 }

 emit done(mFile);

 mActiveJobs--;
 startNextJob();
}

void Job::startNextJob(void)
{
 Job * nextJob;
 nextJob = this;
 do
 {
  nextJob = (Job *)treeWidget()->itemBelow(nextJob);
  if (!nextJob)
     return;
 } while(nextJob->status() != jsQueue);
 nextJob->start();
}

int Job::status(void)
{
 return mStatus;
}

void Job::progressUpdate(int done, int total)
{
 mDone = done;
 mTotal = total;
}

void Job::updateStatus(void)
{
 int delta;
 int speed;
 //if (mDone > mDoneOld)
 //{
  delta = mDone - mDoneOld;
  speed = (delta + mSpeed) / 2;
 //}
 //else
 //{
  //speed = mSpeed;
// }
 setText(2, size2str(mDone));
 setText(5, size2str(mTotal));
 setText(4, size2str(speed) + "/s");
 if (speed > 0)
 {
  setText(3, time2str((mTotal - mDone) / speed));
 }
 mSpeed = speed;
 mDoneOld = mDone;
}

void Job::waitForRestart(void)
{
 if (mTimeout <= 0)
 {
  mTimer->stop();
  disconnect(mTimer, SIGNAL(timeout()), this, 0);
  //qDebug() << "Restarting download....";
  prepareDone(false, true);
  return;
 }
 mTimeout--;
 setText(3, time2str(mTimeout));
}
